package com.github.xzb617.client.config;

import com.github.xzb617.adm.service.ConfigFileService;
import com.github.xzb617.adm.service.FlowService;
import com.github.xzb617.adm.service.FlowStatusService;
import com.github.xzb617.adm.service.RouteRuleService;
import com.github.xzb617.client.config.dto.Config;
import com.github.xzb617.common.util.StrUtil;
import com.github.xzb617.domain.entity.ConfigFile;
import com.github.xzb617.domain.entity.FlowStatus;
import com.github.xzb617.domain.entity.RouteRule;
import com.github.xzb617.security.authc.Anonymous;
import com.github.xzb617.utils.WebUtil;
import com.github.xzb617.utils.YamlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;


/**
 * 客户端请求的端点
 * @author xzb617
 */
@RestController
@RequestMapping("/clients")
public class ClientsEndpoint {

    /**
     * 日志
     */
    private final static Logger LOGGER = LoggerFactory.getLogger(ClientsEndpoint.class);
    /**
     * 超时时间
     */
    private final static long TIMEOUT = 60000L;

    private final ClientsContainer clientsContainer;
    private final ConfigFileService configFileService;
    private final FlowService flowService;
    private final FlowStatusService flowStatusService;
    private final RouteRuleService routeRuleService;


    public ClientsEndpoint(ClientsContainer clientsContainer, ConfigFileService configFileService, FlowService flowService, FlowStatusService flowStatusService, RouteRuleService routeRuleService) {
        this.clientsContainer = clientsContainer;
        this.configFileService = configFileService;
        this.flowService = flowService;
        this.flowStatusService = flowStatusService;
        this.routeRuleService = routeRuleService;
    }

    /**
     * 长轮询监听
     * <p>
     *     clientId规则：config_group:config_name&flow_name
     * </p>
     * @param request
     * @return
     */
    @Anonymous
    @GetMapping("/monitorConfig")
    public DeferredResult<ResponseEntity<Boolean>> monitorConfig(HttpServletRequest request) {
        // 组装 client info
        String ipAddr = WebUtil.getClientIpAddr(request);
        String configGroupName = request.getParameter("config_group");
        String configFileName = request.getParameter("config_name");
        String flowName = request.getParameter("flow_name");
        String routeName = request.getParameter("route_name");
        String clientId = configGroupName + ":" + configFileName + "&" + (flowName==null?"*":flowName) + "&" + (routeName==null?"*":routeName);
        String instanceId = ipAddr + ":" + request.getHeader("instance-id");

        // 打印日志
        LOGGER.info("goalkeeper config receive a listening request from the client {} [{}].", clientId, instanceId);

        // 构建异步结果
        DeferredResult<ResponseEntity<Boolean>> deferredResult = new DeferredResult<>(TIMEOUT);
        deferredResult.onCompletion(() -> {
            this.clientsContainer.removeInstance(clientId, instanceId);
        });
        deferredResult.onTimeout(() -> {
            // 超时返回304状态，表示未有新的变化内容
            ResponseEntity<Boolean> errorResult = ResponseEntity.status(HttpStatus.NOT_MODIFIED).body(Boolean.FALSE);
            deferredResult.setErrorResult(errorResult);
            this.clientsContainer.removeInstance(clientId, instanceId);
        });
        deferredResult.onError((Throwable err) -> {
            this.clientsContainer.removeInstance(clientId, instanceId);
        });

        // 添加到长轮询结果容器中
        this.clientsContainer.store(clientId, instanceId, deferredResult);
        return deferredResult;
    }

    @Anonymous
    @GetMapping("/getConfigStatus")
    public ResponseEntity<Config> getConfigStatus(HttpServletRequest request) {
        // 组装 client info
        String ipAddr = WebUtil.getClientIpAddr(request);
        String configGroupName = request.getParameter("config_group");
        String configFileName = request.getParameter("config_name");
        String flowName = request.getParameter("flow_name");
        String routeName = request.getParameter("route_name");


        // 查询配置中心的配置签名和发布时间
        ConfigFile configFile = this.configFileService.getByClient(configGroupName, configFileName);
        // 查询Flow配置是否发生更改
        FlowStatus flowStatus = this.flowStatusService.getById(flowName);
        // 查询Route配置的更新信息
        RouteRule routeRule = this.routeRuleService.getByName(routeName);

        // 都为null，没有新的配置
        if (configFile==null && flowStatus==null && routeRule==null) {
            return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
        }

        // 返回配置更新信息
        Config result = new Config();
        if (configFile != null) {
            result.setConfigSign(configFile.getSign());
            result.setConfigReleaseTime(configFile.getReleaseTime().getTime());
        }
        if (flowStatus != null) {
            result.setFlowSign("");
            result.setFlowChangedTime(flowStatus.getChangedTime().getTime());
        }
        if (routeRule != null) {
            result.setRouteSign(routeRule.getSign());
            result.setRouteChangedTime(routeRule.getUpdateTime().getTime());
        }
        return ResponseEntity.status(HttpStatus.OK).body(result);
    }

    @Anonymous
    @GetMapping("/getConfig")
    public ResponseEntity<Map> getConfig(HttpServletRequest request) {
        // 组装 client info
        String ipAddr = WebUtil.getClientIpAddr(request);
        String configGroupName = request.getParameter("config_group");
        String configFileName = request.getParameter("config_name");
        String flowName = request.getParameter("flow_name");
        String routeName = request.getParameter("route_name");
        String clientId = configGroupName + ":" + configFileName;
        String instanceId = ipAddr + ":" + request.getHeader("instance-id");

        Map<String, Object> clientConfigMap = new HashMap<>();

        // 配置中心配置
        ConfigFile configFile = this.configFileService.getByClient(configGroupName, configFileName);
        if (configFile!=null && !StrUtil.isEmpty(configFile.getConfigContent())) {
            clientConfigMap.putAll(YamlUtil.ymlToMap(configFile.getConfigContent()));
        }

        // 流控配置
        if (!StrUtil.isEmpty(flowName)) {
            Map<String, Object> flowConfigMap = this.flowService.getClientFlowConfig(flowName);
            if (flowConfigMap != null) {
                clientConfigMap.putAll(flowConfigMap);
            }
        }

        // 路由规则配置
        if (!StrUtil.isEmpty(routeName)) {
            Map<String, Object> routeConfigMap = this.routeRuleService.getClientFlowConfig(routeName);
            if (routeConfigMap != null) {
                clientConfigMap.putAll(routeConfigMap);
            }
        }

        // 打印日志
        LOGGER.info("{}[{}] get config details ===> {}", clientId, instanceId, clientConfigMap.toString());
        return ResponseEntity.status(HttpStatus.OK).body(clientConfigMap);
    }

}
